package edu.nf.vac.vaccination.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.github.pagehelper.PageInfo;
import edu.nf.vac.vaccination.dao.NeedleNumDao;
import edu.nf.vac.vaccination.dao.PeriodDao;
import edu.nf.vac.vaccination.dto.NeedleNumDTO;
import edu.nf.vac.vaccination.dto.NeedleNumPageDTO;
import edu.nf.vac.vaccination.dto.PeriodDTO;
import edu.nf.vac.vaccination.entity.NeedleNum;
import edu.nf.vac.vaccination.entity.Period;
import edu.nf.vac.vaccination.exception.DataAccessException;
import edu.nf.vac.vaccination.exception.NeedleNumException;
import edu.nf.vac.vaccination.inf.NeedleNumService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

/**
 * @Author : PYY
 * @create 2022/3/30
 */
@Slf4j
@DubboService(interfaceClass = NeedleNumService.class,version = "1.0.0")
@Transactional(rollbackFor = RuntimeException.class)
public class NeedleNumServiceImpl implements NeedleNumService {

    @Autowired
    private NeedleNumDao needleNumDao ;
    @Autowired
    private PeriodDao periodDao ;

    /**
     * 创建针次，包括该针次的接种时段以及不同时段可接种的疫苗
     * @param dto
     */
    @Override
    public void addNeedleNum(NeedleNumDTO dto) {
        try {
            NeedleNum needleNum = BeanUtil.copyProperties(dto, NeedleNum.class);
            // 减产是否已经创建针次，如果为创建则保存
            Integer nid = needleNumDao.checkNeedleNum(needleNum);
            if(nid == null){
                //计算疫苗总数
                int total = vaccineTotal(dto);
                //设置疫苗总数
                needleNum.setVaccineTotal(total);
                //设置剩余总数
                needleNum.setVaccineOver(total);
                //保存针次
                needleNumDao.saveNeeldNum(needleNum);
                //给每个时段的信息设置针次的id
                List<Period> periodList = setNidToPeriods(needleNum.getPeriods(), needleNum.getNid());
                //批量插入放号时段
                periodDao.savePeriod(periodList);
            }else{
                throw new NeedleNumException("当前日期的针次已存在，是否更新该记录");
            }
        } catch (NeedleNumException e) {
            throw e ;
        }catch (RuntimeException e) {
            log.error(e.getMessage(),e);
            throw new DataAccessException("新建针次失败，请稍后重试");
        }
    }

    /**
     * 更新针次与时段信息
     * @param dto
     */
    @Override
    public void updateNeedleNum(NeedleNumDTO dto) {

        try {
            NeedleNum needleNum = BeanUtil.copyProperties(dto, NeedleNum.class);
            Integer nid = needleNumDao.checkNeedleNum(needleNum);
            needleNum.setNid(nid);
            //重新计算疫苗总数并更新针次信息
            int total = vaccineTotal(dto);
            needleNum.setVaccineTotal(total);
            needleNum.setVaccineOver(total);
            needleNumDao.updateVaccine(needleNum);
            //删除原各个时段的疫苗信息并重新设置
            periodDao.delPeriods(nid);
            //给每个时段信息设置针次的id
            List<Period> periodList = setNidToPeriods(needleNum.getPeriods(), nid);
            //批量插入放号时段
            periodDao.savePeriod(periodList);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            throw new DataAccessException("更新失败，请稍后重试");
        }

    }

    /**
     * 给每个时段信息设置针次的id
     * @param periodList
     * @param nid
     * @return
     */
    private List<Period> setNidToPeriods(List<Period> periodList,Integer nid){
        periodList.forEach(period -> period.setNeedleNumId(nid));
        return periodList ;
    }

    /**
     * 计算疫苗总数
     * @param dto
     * @return
     */
    private int vaccineTotal(NeedleNumDTO dto){
        return dto.getPeriods().stream()
                .filter(periodDTO -> Objects.nonNull(periodDTO.getVaccineTotal()))
                .mapToInt(PeriodDTO::getVaccineTotal)
                .sum() ;
    }

    /**
     * 疫苗总量自减
     * @param period
     * @param needleNumId
     */
    @Override
    public void decrTotal(String period, Integer needleNumId) {
        if(periodDao.decrTotal(period, needleNumId)==0){
            throw new NeedleNumException("预约已满，请查看其它时间段");
        }
        try {
            needleNumDao.decrTotal(needleNumId);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            throw new DataAccessException("扣减失败，请稍后重试");
        }
    }

    /**
     * 针次列表信息查询
     * @param dto 查询条件
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public NeedleNumPageDTO listNeedleNum(NeedleNumDTO dto, Integer pageNum, Integer pageSize) {
        NeedleNum needleNum = BeanUtil.copyProperties(dto, NeedleNum.class);
        List<NeedleNum> list = needleNumDao.listNeedleNum(needleNum, pageNum, pageSize);
        PageInfo<NeedleNum> pageInfo = new PageInfo(list);
        NeedleNumPageDTO pageDTO = BeanUtil.copyProperties(pageInfo, NeedleNumPageDTO.class);
        return pageDTO;
    }

    /**
     * 依据不同条件查询针次信息（app端查询）
     * @param dto
     * @return
     */
    @Override
    public List<NeedleNumDTO> searchNeedleNum(NeedleNumDTO dto) {
        if(dto.getNeedleNum() == null) {
            dto.setNeedleNum(1);
        }
        if(dto.getInoculateDate() == null) {
            dto.setInoculateDate(DateUtil.parse(DateUtil.today())) ;
        }
        NeedleNum needleNum = BeanUtil.copyProperties(dto, NeedleNum.class);
        List<NeedleNum> list = needleNumDao.searchNeedleNum(needleNum);
        return BeanUtil.copyToList(list, NeedleNumDTO.class);
    }

    @Override
    public List<PeriodDTO> listPeriodByNeedleNumId(Integer needleNumId) {
        List<Period> list = periodDao.listPeriodByNeedleNumId(needleNumId);
        return BeanUtil.copyToList(list, PeriodDTO.class);
    }
}